package cn.wolfcode.service.impl;

import cn.wolfcode.common.constants.CommonConstants;
import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CommonCodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.Product;
import cn.wolfcode.domain.SeckillProduct;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.mapper.SeckillProductMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.DateUtil;
import cn.wolfcode.util.UserUtil;
import cn.wolfcode.web.feign.ProductFeignApi;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.http.handler.RequestHandler;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * Created by lanxw
 */
@Service
public class SeckillProductServiceImpl implements ISeckillProductService {
    @Autowired
    private SeckillProductMapper seckillProductMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private ProductFeignApi productFeignApi;
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private IOrderInfoService orderInfoService;

    @Override
    public List<SeckillProductVo> queryByTime(Integer time) {
        //1.根据场次id查询秒杀商品对象
        List<SeckillProduct> seckillProductList = seckillProductMapper.queryCurrentlySeckillProduct(time);
        //2.遍历秒杀商品对象，拿到商品对象的id
        List<Long> productIds = new ArrayList<>();
        for (SeckillProduct seckillProduct : seckillProductList) {
            productIds.add(seckillProduct.getProductId());
        }
        //调用商品服务的接口，把productIds作为参数进行调用，拿到商品的对象信息
        Result<List<Product>> productList = productFeignApi.queryByProductIds(productIds);
        if (productList == null ||  productList.hasError()){
            //远程商品服务不可用，走降级方法返回null
            //远程商品服务可用，但是内部出错了，返回result对象，但是状态码不为200
            throw new BusinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);
        }
        //正常的，远程商品服务可用，返回状态码为200,远程返回商品信息
        List<Product> products = productList.getData();
        Map<Long,Product> map = new HashMap<>();
        for (Product product : products) {
            //把商品信息<productId,product>信息封装到map中，建立映射关系，根据商品ID查询到商品信息
            map.put(product.getId(),product);
        }
        //把商品服务信息和秒杀服务信息封装到vo对象中
        List<SeckillProductVo> seckillProductVo = new ArrayList<>();
        for (SeckillProduct seckillProduct : seckillProductList) {
            //通过秒杀对象里面的ProductId作为key,拿到map中key对应的对象信息
            Product product = map.get(seckillProduct.getProductId());
            SeckillProductVo vo = new SeckillProductVo();
            //把product属性注入到vo中
            BeanUtils.copyProperties(product,vo);
            //baseckillProduct属性注入到vo中
            BeanUtils.copyProperties(seckillProduct,vo);
            //设置最新库存信息到vo中
            vo.setCurrentCount(vo.getStockCount());
            seckillProductVo.add(vo);
        }
        return seckillProductVo;
    }

    @Override
    public SeckillProductVo find(Integer time, Long seckillId) {
        //从redis中拿出秒杀场次的信息
        String realKey = SeckillRedisKey.SECKILL_PRODUCT_HASH.getRealKey(String.valueOf(time));
        //拿到了信息，再从信息中根据商品ProductId去拿到对象的详细信息。
        String product = (String) redisTemplate.opsForHash().get(realKey, seckillId.toString());
        return JSON.parseObject(product,SeckillProductVo.class);
    }

    @Override
    public Result<String> doSeckill(Integer time, Long seckillId, HttpServletRequest request) {
        SeckillProductVo seckillProductVo = seckillProductService.find(time, seckillId);
        //1.时间判断，需要到达秒杀时间才能进行秒杀操作
        boolean legalTime = DateUtil.isLegalTime(seckillProductVo.getStartDate(), seckillProductVo.getTime());
        /*if (!legalTime){
            return Result.error(CommonCodeMsg.ILLEGAL_OPERATION);
        }*/
        //2.登录之后才能进行抢购
        //3.1个用户针对一场秒杀，只能抢购一个商品，根据用户id,(手机号)秒杀场次ID
        String token = request.getHeader(CommonConstants.TOKEN_NAME);
        String phone = UserUtil.getUserPhone(redisTemplate, token);
        //判断Redis是否存在  seckillId:phone这样的key,如果存在说明下过该订单
        String realKey = SeckillRedisKey.SECKILL_ORDER_SET.getRealKey(String.valueOf(seckillId));
        if(redisTemplate.opsForSet().isMember(realKey,phone)){
            return Result.error(SeckillCodeMsg.REPEAT_SECKILL);
        }
        //控制秒杀的人数
        String stockCountKey = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time.toString());
        //利用原子性递减，返回值是减完之后的数值
        Long remainCount = redisTemplate.opsForHash().increment(stockCountKey, String.valueOf(seckillId), -1);
        if (remainCount < 0){
            return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        System.out.println("执行秒杀逻辑");
        //改成MQ异步下单
        OrderMessage orderMessage = new OrderMessage(time,seckillId,token,Long.parseLong(phone));
        //进入队列等待
        rocketMQTemplate.syncSend(MQConstant.ORDER_PEDDING_TOPIC,orderMessage);
//        String order = orderInfoService.doSeckill(seckillProductVo,phone);
        return Result.success("进入下单队列，请耐心等待结果");
    }

    @Override
    public int derStock(Long seckillId) {
        return seckillProductMapper.decrStock(seckillId);
    }

    @Override
    public List<SeckillProductVo> queryByTimeFromCache(Integer time) {
        String realKey = SeckillRedisKey.SECKILL_PRODUCT_HASH.getRealKey(String.valueOf(time));
        List<Object> values = redisTemplate.opsForHash().values(realKey);
        List<SeckillProductVo> seckillProductVos = JSON.parseArray(values.toString(), SeckillProductVo.class);
        return seckillProductVos;
    }

    @Override
    public void syncStorckCountToRedis(Integer time, Long seckillId) {
        int stockCount = seckillProductMapper.getStockCount(seckillId);
        if (stockCount > 0){
            //说明库存没有卖完
            String realKey = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time.toString());
            //将数据库的库存同步到Redis中
            redisTemplate.opsForHash().put(realKey,seckillId.toString(),String.valueOf(stockCount));
        }
    }
    @Override
    public void incrStockCount(Long seckillId) {
        seckillProductMapper.incrStock(seckillId);
    }
}